﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Drawing;
using System.IO;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.ComponentModel.DataAnnotations;
using System.Globalization;

namespace Pikapika.Controllers
{
    public class ThumbnailController : Controller
    {
        private const long TicksPerSecond = 0x989680L;

        public ActionResult Generate(string pathInfo, [Range(1, int.MaxValue)] int maxWidth, [Range(1, int.MaxValue)] int maxHeight)
        {
            if (maxWidth <= 0 || maxHeight <= 0)
            {
                return HttpNotFound();
            }
            Image img;
            string fullPath;
            DateTime lastModified;
            string etag;
            try
            {
                fullPath = Server.MapPath("~/" + pathInfo);
                lastModified = new FileInfo(fullPath).LastWriteTimeUtc;
                etag = GenerateETag(lastModified, DateTime.UtcNow);
            }
            catch
            {
                return HttpNotFound();
            }
            Response.Headers.Add("X-Execution-Time", DateTime.UtcNow.ToString("u"));
            Response.AddFileDependency(fullPath);
            Response.Cache.SetCacheability(HttpCacheability.Public);
            Response.Cache.SetMaxAge(Properties.Settings.Default.ImageThumbnailClientCacheDuration);
            Response.Cache.SetExpires(DateTime.Now + Properties.Settings.Default.ImageThumbnailClientCacheDuration);

            // hack: sometimes max-age is not added
            Response.Headers.Add("Cache-Control", "max-age=" + ((int)Properties.Settings.Default.ImageThumbnailClientCacheDuration.TotalSeconds).ToString());

            var ifModifiedSince = Request.Headers["If-Modified-Since"];

            if (!string.IsNullOrWhiteSpace(ifModifiedSince))
            {
                DateTime ifModifiedSinceTime;
                if (DateTime.TryParse(ifModifiedSince, null, DateTimeStyles.AssumeUniversal, out ifModifiedSinceTime))
                {
                    var difference = lastModified - ifModifiedSinceTime.ToUniversalTime();
                    if (difference.TotalSeconds <= 1)
                    {
                        Response.Cache.SetLastModified(lastModified);
                        Response.Cache.SetETag(etag);
                        return new HttpStatusCodeResult(304, "Not Modified");
                    }
                }
            }

            try
            {
                img = Image.FromFile(fullPath);
            }
            catch
            {
                return HttpNotFound();
            }
            Response.Cache.SetLastModified(lastModified);
            Response.Cache.SetETag(etag);
            var ms = new MemoryStream();
            using (img)
            {
                if ((img.Width <= maxWidth && img.Height <= maxHeight))
                {
                    return File(fullPath, MimeMapping.GetMimeMapping(fullPath));
                }
                int targetWidth, targetHeight;
                if ((decimal)maxWidth / maxHeight < (decimal)img.Width / img.Height)
                {
                    targetWidth = maxWidth;
                    targetHeight = Math.Max(1, (int)Math.Round((decimal)maxWidth * img.Height / img.Width));
                }
                else
                {
                    targetHeight = maxHeight;
                    targetWidth = Math.Max(1, (int)Math.Round((decimal)maxHeight * img.Width / img.Height));
                }
                using (var bm = new Bitmap(targetWidth, targetHeight))
                {
                    using (var g = Graphics.FromImage(bm))
                    {
                        g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                        g.FillRectangle(Brushes.White, 0, 0, targetWidth, targetHeight);
                        g.DrawImage(img, 0, 0, targetWidth, targetHeight);
                    }
                    bm.Save(ms, ImageFormat.Jpeg);
                }
            }
            if (ms.Length > new FileInfo(fullPath).Length)
            {
                // no point to return a bigger file
                return File(fullPath, MimeMapping.GetMimeMapping(fullPath));
            }
            ms.Seek(0, SeekOrigin.Begin);
            return File(ms, "image/jpeg");
        }

        private static string GenerateETag(DateTime lastModified, DateTime now)
        {
            long num = lastModified.ToFileTime();
            long num2 = now.ToFileTime();
            string str = num.ToString("X8", CultureInfo.InvariantCulture);
            if ((num2 - num) <= 0x1c9c380L)
            {
                return ("W/\"" + str + "\"");
            }
            return ("\"" + str + "\"");
        }
    }
}
